#pragma once

#include"Logger.hpp"
#include<iostream>
#include<string>
#include<unordered_map>
#include<sstream>
#include<fstream>

static const std::string linesep = "\r\n";
static const std::string innersep1 = " ";
static const std::string innersep2 = ": ";
static const std::string webroot = "./wwwroot";
static const std::string defaulthome = "index.html";
static const std::string html_404 = "404.html";
static const std::string suffixsep = ".";

//在这个文件中实现HTTP协议中的请求与应答,并完成其序列化与反序列化
class HttpRequest
{
private:
    std::string ReadOneLine(std::string& reqstr , bool* status)
    {
        //读取到每一行就在reqstr 中删掉读取的这一行
        auto pos = reqstr.find(linesep);
        if(pos == std::string::npos)
        {
            *status = false;
            return std::string();
        }

        //读取成功
        *status = true;
        auto line = reqstr.substr(0, pos);
        reqstr.erase(0,pos+linesep.size());
        return line;
    }
    void ParseReqLine(std::string& reqline)
    {
        //请求方法 url 版本
        // auto pos = reqline.find(innersep1);
        // if(pos)
        //还可以使用stringstream, 以空格作为分隔符
        std::stringstream ss(reqline);
        ss>>_method >> _uri >> _httpversion; 
    }
    void BuildKV(std::string& line ,std::string* k , std::string* v)
    {
        //将提取到的kv数据带出去
        //以: 作为分隔符 innersep2
        auto pos = line.find(innersep2);
        if(pos==std::string::npos)
        {
            *k  = *v  = std::string();
            return;
        }
        //成功找到，做切割
        *k = line.substr(0 , pos);
        *v = line.substr(pos+innersep2.size());
    }
public:
    //请求是浏览器发给服务器的，所以我们不用处理序列化，实现反序列化就可以了
    // bool Serialize()
    // {}
    bool DeSerialize(std::string& reqstr)//HTTP自己有处理报文粘包问题，我们将当作只要拿到报文数据就是完整的报文
    {
        bool status = true;//表示读取一行结束，未读到空行
        //获取第一行，提取其中的数据
        std::string reqline = ReadOneLine(reqstr , &status);
        if(reqline.empty()) 
            return false;

        ParseReqLine(reqline);
        //debug
        // LOG(LogLevel::DEBUG) << "_method: " << _method;
        // LOG(LogLevel::DEBUG) << "_uri: " << _uri;
        // LOG(LogLevel::DEBUG) << "_httpversion: " << _httpversion;
        //接下来是获取剩余报头信息，直到读到空行
        //增加一个状态来描述是否将报头读取结束,读到空行返回空串
        while(true)
        {
            status = true;
            reqline = ReadOneLine(reqstr , &status);

            if(status && !reqline.empty())//获取到了kv结构的字符串
            {
                std::string k,v;
                BuildKV(reqline , &k , & v);
                //kv均不为空才放入_req_handers 中
                if(k.empty() || v.empty())
                {
                   continue;
                }
                //  _req_handers[k]=v;
                _req_handers.insert(std::make_pair(k,v));
            }
            else if(status && reqline.empty())
            {
                //为空且状态为true->读到了空行
                _blank_line = linesep;
                break;//退出循环
            }
            else 
            {
                 //读取失败
                // return false;
                break;
            }
        }
        
        //获取完了报头，可以debug 一下
        // for(auto&e : _req_handers)
        // {
        //     LOG(LogLevel::DEBUG) << e.first <<"#:" << e.second ;
        // }
        //后面还有数据就是正文
        _req_body = reqstr;
        //反序列化成功之后，服务端最关心的是客户端访问的什么资源
        //约定，当uri 只有/ ，意思就是请求服务器首页；当uri 带了指定路径意思就是访问服务器web 根目录下的指定路径中的文件
        //对访问路径做提取处理，增加一个成员属性 - > _path
        if(_uri=="/")
        {
            _path = webroot + _uri + defaulthome;
        }
        else{
            //访问指定路径下的文件
            _path = webroot + _uri;
        }
        LOG(LogLevel::DEBUG) << "_path: " <<_path;
        return true;
    }

    std::string Path()
    {
        return _path;
    }
    // void SetPath(const std::string path)
    // {
    //     _path = path;
    // }
    
    //获取请求资源的后缀
    std::string Suffix()
    {
        //先判断是否有访问的资源
        if(_path.empty())
        {
            return std::string();
        }
        else{
            auto pos = _path.find(suffixsep);
             if(pos == std::string::npos) return std::string();
             else return _path.substr(pos);//.html
        }
    }
private:
    //请求方法 url 版本
    std::string _method;
    std::string _uri;
    std::string _httpversion;
    std::unordered_map<std::string, std::string> _req_handers;//存放kv结构报头属性
    std::string _blank_line;//区分报头与正文的空行
    std::string _req_body;//正文

    std::string _path;//访问资源的目标路劲
};

class HttpResponse
{
private:
    std::string Code2Desc(int code)
    {
        switch (code)
        {
            case 200:
            {
                return "OK";
            }
            case 400:
            {
                return "Bad Request";
            }
            case 404:
            {
                return "Not Found";
            }
            default:
                return "";
        }
    }
public:
    HttpResponse():_httpversion("HTTP/1.1") , _blank_line("\r\n")
    {}
    bool SetHeader(const std::string& key , const std::string&value)
    {
        _resp_handers[key] = value;
        return true;
    }
    //应答，服务器发给浏览器，反序列化的过程浏览器自己解决，实现应答的序列化就好
    //序列化 ：将格式化数据转换为一个长字符串
    std::string Serialize()
    {
        //版本 状态码 状态码格式
        std::string respstr = _httpversion + innersep1 + std::to_string(_code) + innersep1 + _desc + linesep;
        //kv结构处理
        //添加报头属性 Content-Length，设置接口暴露出去使用 --> 有正文就设置Content-Length
        if(!_resp_body.empty())
        {
            std::string len = std::to_string(_resp_body.size());
            SetHeader("Content-Length" , len);
        }

        for(auto&e : _resp_handers)
        {
            std::string line = e.first + innersep2 + e.second + linesep;
            respstr += line;
        }
        //加上空行
        respstr += _blank_line;
        //加上正文
        respstr += _resp_body;

        return respstr;
    }

    //二进制形式读取文件中的数据到内存中
    bool ReadContent(const std::string&path)
    {
        //使用ifstream
        std::ifstream file(path, std::ios::binary);
        if(!file.is_open())
        {
            // throw std::runtime_error("无法打开文件："+path);
            LOG(LogLevel::WARNING) << path << " 资源不存在";
            return false;
        }

        //定位到文件结尾获取文件大小
        file.seekg(0,std::ios::end);
        std::streampos fileSize = file.tellg();//提取文件大小
        file.seekg(0 , std::ios::beg);

        //创建足够大的空间
        _resp_body.resize(static_cast<size_t>(fileSize));

        //读取我呢间内容到字符串中
        file.read(&_resp_body[0] ,fileSize);

        file.close();//关闭文件
        return true;
    }
    // void SetCode(int code ,const std::string&desc)//状态码描述本省就是描述状态码的信息，是固定的，无需我们手动传递设置
    // {
    //     _code = code;
    //     _desc = desc;
    // }
    void SetCode(int code)
    {
        //code 本身就有范围的，即1开头到5开头
        if(code >= 100 && code < 600)
        {
            _code = code;
            _desc = Code2Desc(code);
        }
        else{
            //范围不合法就不设置
            LOG(LogLevel::ERROR) << "状态码非法: " << code;
        }
    }

    //关于应答的正文类型是通过资源的后缀来区分的，在Content-Type 对照表中进行查找
    //可以增加一个配置文件为Content-Type 中的内容，获取文件后缀得到想要的内容；此处简单实现，枚举几个就好
    //有正文与否，有正文就获取资源文件的后缀， 从 _path 中获取；再从我们自己的对照表中获取Content-Type,最后再设置报头属性

    // bool DeSerialize()
    // {}
private:
    //版本 状态码 状态码描述
    std::string _httpversion;
    int _code;
    std::string _desc;
    std::unordered_map<std::string, std::string> _resp_handers;
    std::string _blank_line;
    std::string _resp_body;
};

//整合功能
class Http
{
private:
        //通过资源文件后缀获得Content-Type
    std::string Suffix2Desc(const std::string& suffix)
    {
        if(suffix == ".html")
            return "text/html ";
        else if(suffix == ".css")
            return "text/css";
        else if(suffix == ".js")
            return "application/x-javascript";
        else if(suffix == ".png")
            return "image/png";
        else if(suffix == ".jpg")
            return "image/jpeg";
        else if(suffix == ".txt")
            return "text/plain";
        else 
            return "text/html";
    }
public:
    std::string HandlerRequest(std::string & requeststr)//在此处获取404页面，当读取指定路径下的文件失败的时候，就去获取404页面
    {
        std::string respstr;
        //反序列化请求
        HttpRequest req; 
        // LOG(LogLevel::DEBUG) << "反序列化请求报文" ;
        if(req.DeSerialize(requeststr))
        {
            //反序列化成功，构建应答，序列化再返回
            HttpResponse resp;
            //1、将所要访问资源的文件以二进制形式打开放在内存中
            if(resp.ReadContent(req.Path()))
            {
                //2、简单设置状态码
                resp.SetCode(200);   
                //设置Content-Type
                std::string suffix =req.Suffix();//获取资源后缀
                resp.SetHeader("Content-Type",Suffix2Desc(suffix));
            }
            else{
                //获取404页面,设置path
                std::string err_404 = webroot + "/" + html_404;
                // req.SetPath(err_404);
                resp.ReadContent(err_404);
                resp.SetCode(404);
                //设置404页面的Content-Type
                std::string suffix = req.Suffix();
                resp.SetHeader("Content-Type" , Suffix2Desc(suffix));
            }
         
            //3、序列化
            respstr = resp.Serialize();
        }
        std::cout << respstr  << std::endl;
        return respstr;//debug 
    }
};